﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Configuration;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Data;

using Estimate.Common.DataFileFinder;
using Estimate.Common.DataImporter;
using Estimate.Common.Estimate;
using Estimate.Common.Entity;
using Estimate.Common.DBFieldTypes;
using Estimate.Common.Repository;
using log4net;
using OperateLogLibrary;

namespace Estimate.Common
{
    /// <summary>
    /// 将导入数据文件及后续处理的函数整合在一起，方便调用方调用
    /// </summary>
    public class DataImporterCombiner
    {
        public  Semier semier;

        public static ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private OperateLog log;
        public OperateLog Log
        {
            set
            {
                log = value;
            }
            get
            {
                if (log == null)
                    return new OperateLog();
                else
                    return log;
            }
        }

        public NameValueCollection AppSettings;
        private string AppStartPath;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dbConnectionString">数据库连接字符串</param>
        public DataImporterCombiner(NameValueCollection appSettings)
        {
            AppSettings = appSettings;
            semier = new Semier(AppSettings["ConnectionString"]);
        }

        public DataImporterCombiner(NameValueCollection appSettings, string appStartPath)
        {
            AppSettings = appSettings;
            AppStartPath = appStartPath;
            semier = new Semier(AppSettings["ConnectionString"]);
        }

        public void Execute(DateTime dt)
        {
            string dbConnectionString = AppSettings["ConnectionString"];

            ImportForeData(dbConnectionString, DateTime.MinValue, AppStartPath);//预报产品数据入库

            //ImportForecasterFore(dbConnectionString, dt);//预报员数据入库

            ImportRealData(dbConnectionString, dt.AddDays(-1));//实况数据入库

            ImportNewForeData(dbConnectionString, dt, AppStartPath);//经过平均误差修正后的预报产品数据入库

            ImportOptimalForeData(dbConnectionString, dt, AppStartPath);//集成最优预报数据入库
        }

     
        #region 老代码
        /// <summary>
        /// 导入实况数据
        /// </summary>
        /// <param name="info">导入数据信息</param>
        /// <param name="dt">日期</param>
        /// <param name="dirPath">目录</param>
        /// <param name="finder">文件发现类</param>
        /// <param name="importer">文件导入类</param>
        public void ImportData(string info, DateTime dt, string dirPath, IDataFileFinder finder, IDataImporter importer, bool assertHaseFile)
        {
            Logger.Info(info);
            Logger.Info(string.Format("文件路径: {0}", dirPath));
            Log.SetLog(string.Format("文件路径: {0}", dirPath));
           
            int count = 0;
            foreach (string f in finder.GetFileFromDirectory(dt, dirPath))
            {
                if (f.Length == 0)
                {
                    continue;
                }
                Logger.Info(string.Format("发现文件: {0}, 正在处理", f));
                Log.SetLog(string.Format("发现文件: {0}, 正在处理", f));
                try
                {
                    importer.ImportDataFromFileWithDate(f, dt);
                }
                catch (Exception e)
                {

                    string errorMsg = string.Format("处理文件时 {0}  时,出现问题： {1}", f, e.Message);
                    string trackMsg = string.Format("StackTrace ： {0}", e.StackTrace.ToString());

                    Logger.Error(errorMsg);
                    Logger.Error(trackMsg);
                    Log.SetLog(errorMsg);
                    Log.SetLog(trackMsg);

                }
                count++;
            }
            if (count == 0)
            {
                if (assertHaseFile)
                {
                    Logger.Info(string.Format("未能发现需要的数据文件： {0} {1}", dt.ToString(), info));
                    Log.SetLog(string.Format("未能发现需要的数据文件： {0} {1}", dt.ToString(), info));
                }
                else
                {
                    Logger.Info(string.Format("未能发现需要的数据文件： {0} {1}", dt.ToString(), info));
                    Log.SetLog(string.Format("未能发现需要的数据文件： {0} {1}", dt.ToString(), info));
                }
            }

        }

        public void ImportManualData(int startYear, int startMonth, int endYear, int endMonth,bool needCalculateSemi = true)
        {
            // 人工站实况数据入库
            DateTime start = new DateTime(startYear, startMonth, 1);
            DateTime end = new DateTime(endYear, endMonth, 1);
            string dirPath = AppSettings["ManualStationDirectory"];
            string dbConnectionString = AppSettings["ConnectionString"];
            IDataFileFinder finder = new ManualStationFinder();
            
            IDataImporter importer = DataImporterFactory.GetManualStationDataImporter(dbConnectionString);
            if (needCalculateSemi)
                importer.AfterDataInDatabase += new EventHandler(CalculateManualSemi);

            DateTime cur = start;
            while (cur <= end)
            {
                ImportData("手动站 实况数据入库", cur, dirPath, finder, importer,false);
                cur = cur.AddMonths(1);
            }
        }

        public void ImportSchedule(int startYear, int startMonth, int endYear, int endMonth, bool forceClear)
        {
            DateTime start = new DateTime(startYear, startMonth, 1);
            DateTime end = new DateTime(endYear, endMonth, 1);
            string dbConnectionString = AppSettings["ConnectionString"];

            if (forceClear)
            {
                ScheduleLogRepository logRepository = new ScheduleLogRepository(dbConnectionString);
                logRepository.DeleteRecordWithDateRange( start, end );

                ScheduleRepository repository = new ScheduleRepository(dbConnectionString);
                repository.DeleteRecordWithDateRange(start, end.AddMonths(1).AddDays(-1));
            }


            string dirPath = AppSettings["ScheduleDirectory"];
            IDataFileFinder finder = new ScheduleFinder();
            ScheduleImporter importer = (ScheduleImporter)DataImporterFactory.GetScheduleDataImporter(dbConnectionString);
            importer.Log = this.Log;
            DateTime cur = start;
            while (cur <= end)
            {
                importer.Clear();
                ImportData("排班表入库", cur, dirPath, finder, importer, false);
                importer.SaveEntity(forceClear);
                cur = cur.AddMonths(1);
            }
        }

        public void ReimportInvalideData()
        {
            string dbConnectionString = AppSettings["ConnectionString"];
            RealDataLogRepository realDataRepository = new RealDataLogRepository(dbConnectionString);
            if (!realDataRepository.IsTableExist())
                realDataRepository.CreateTable();
            //自动站实况数据入库

            IDataImporter importer = DataImporterFactory.GetAutoStationDataImporter(dbConnectionString);
            importer.AfterDataInDatabase += new EventHandler(CalculateAutoSemi);
            // GetLatestRealData
            RealDataLogRepository logRepository = new RealDataLogRepository(dbConnectionString);
            RealDataLogEntity entity = logRepository.GetLatestRealData(ForeSemiType.Auto);

            //获取重新入库的天数
            string backDate = AppSettings["ReimportBackDate"];

            DateTime lastDate = entity.RealDate;
            DateTime beginDate = lastDate.AddDays(0 - Convert.ToInt32(backDate));
            DateTime endDate = DateTime.Now;
            RealDataLogEntity[] records = logRepository.GetRealDataWithDateRange(ForeSemiType.Auto, beginDate, endDate);
            Logger.Info("重导自动站实况");
            Log.SetLog("重导自动站实况");
            DateTime curDate = beginDate;
            List<DateTime> missedDate = new List<DateTime>();
            foreach (RealDataLogEntity record in records)
            {
                while (curDate.Year != record.RealDate.Year || curDate.Month != record.RealDate.Month || curDate.Day != record.RealDate.Day)
                {
                    missedDate.Add(curDate.Date);
                    curDate = curDate.AddDays(1);
                }
                curDate = curDate.AddDays(1);

                if (record.NeedReimport)
                {
                    Logger.Info("导入文件" + record.FilePath);
                    Log.SetLog("导入文件" + record.FilePath); 
                    importer.ImportDataFromFile(record.FilePath);
                }

            }

            while (curDate <= endDate)
            {
                missedDate.Add(curDate.Date);
                curDate = curDate.AddDays(1);
            }


            //导入缺失数据
            string dirPath = AppSettings["AutoStationDirectory"];
            IDataFileFinder finder = new AutoStationFinder();
            foreach (DateTime dt in missedDate)
            {
                ImportData("重新入库缺失的自动站实况数据", dt, dirPath, finder, importer, false);
            }
        }

        public void ImportRealData(string dbConnectionString, DateTime dt,bool needCalculateSemi = true, string configItem = "AutoStationDirectory")
        {
            //自动站实况数据入库
            string dirPath = AppSettings[configItem];
            IDataFileFinder finder = new AutoStationFinder();
            IDataImporter importer = DataImporterFactory.GetAutoStationDataImporter(dbConnectionString);
            if( needCalculateSemi)
                importer.AfterDataInDatabase += new EventHandler(CalculateAutoSemi);
            ImportData("自动站实况数据入库", dt, dirPath, finder, importer, true);
        }

        public void ImportRealData(DateTime start, DateTime end, bool needCalculateSemi = true)
        {
            // 人工站实况数据入库            
            string dirPath = AppSettings["AutoStationDirectory"];
            string dbConnectionString = AppSettings["ConnectionString"];
            IDataFileFinder finder = new AutoStationFinder();

            IDataImporter importer = DataImporterFactory.GetAutoStationDataImporter(dbConnectionString);
            if (needCalculateSemi)
                importer.AfterDataInDatabase += new EventHandler(CalculateAutoSemi);

            DateTime cur = start;
            while (cur <= end)
            {
                ImportData("自动站 实况数据入库", cur, dirPath, finder, importer, true);
                cur = cur.AddDays(1);
            }
        }

        /// <summary>
        /// 预报员预报数据入库
        /// </summary>
        /// <param name="dbConnectionString"></param>
        /// <param name="dt"></param>
        /// <param name="configItem"></param>
        public void ImportForecasterFore(string dbConnectionString, DateTime dt, string configItem = "UserSiteForeDirectory")
        {
            //预报员预报数据预报文件
            //获取所有子目录
            IDataFileFinder finder = new UserSiteForeFinder();

            string dirPath = AppSettings[configItem];
            DirectoryInfo thisOne = new DirectoryInfo(dirPath);

            foreach (DirectoryInfo sub in thisOne.GetDirectories())
            {
                string userdir = string.Format(@"{0}\{1}\", sub.FullName, dt.Year.ToString());

                if (Directory.Exists(userdir))
                {
                    //需要先检查一下该用户是否存在
                    IDataImporter importer = DataImporterFactory.GetForecasterDataImporter(dbConnectionString, sub.Name);
                    ImportData(string.Format("预报员个人 {0} 的预报文件！", sub.FullName), dt, sub.FullName, finder, importer, false);
                }
            }
        }

        public void CalculateAutoSemi(object sender, EventArgs args)
        {
            Logger.Info("计算Semi");
            Log.SetLog("计算Semi"); 

            FindDataEventArgs findArgs = args as FindDataEventArgs;
            if (findArgs.Data is RainRealEntity)
            {
                RainRealEntity rainEntity = findArgs.Data as RainRealEntity;
                semier.CalculatSemiWithRainRecord(rainEntity, ForeSemiType.Auto, ForeSemiType.Auto);
            }
            else
            {
                TempRealEntity tempEntity = findArgs.Data as TempRealEntity;
                semier.CalculatSemiWithTempRecord(tempEntity, ForeSemiType.Auto, ForeSemiType.Auto);
            }
        }

        public void CalculateManualSemi(object sender, EventArgs args)
        {
            FindDataEventArgs findArgs = args as FindDataEventArgs;
            if (findArgs.Data is RainRealEntity)
            {
                RainRealEntity rainEntity = findArgs.Data as RainRealEntity;
                semier.CalculatSemiWithRainRecord(rainEntity, ForeSemiType.Auto, ForeSemiType.Manual);
            }
            else
            {
                TempRealEntity tempEntity = findArgs.Data as TempRealEntity;
                semier.CalculatSemiWithTempRecord(tempEntity, ForeSemiType.Auto, ForeSemiType.Manual);
            }
        }

        /// <summary>
        /// 初足预报员预报数据
        /// </summary>
        public void CompleteForecasterForeLackFile(DateTime start, DateTime end, bool deleteOldData)
        {

            string dbConnectionString = AppSettings["ConnectionString"];

            string dirPath = AppSettings["ForecasterForeDirectory"];
            DirectoryInfo thisOne = new DirectoryInfo(dirPath);
            List<ForeRepository> repositories = new List<ForeRepository>();
            foreach (DirectoryInfo sub in thisOne.GetDirectories())
            {
                ForeRepository rep = ForeRepositoryFactory.ForecasterRepository(dbConnectionString, sub.Name);
                if (deleteOldData)
                    rep.DeleteRecordWithDateRange(start, end);
                repositories.Add(rep);
            }

            DateTime cur = start;
            while (cur <= end)
            {
                ImportForecasterFore(dbConnectionString, cur, AppStartPath);
               
                cur = cur.AddDays(1);
            }

            ReCalSemi(start, end,dbConnectionString,repositories);
        }

        public void ReCalSemi(DateTime start, DateTime end,string dbConnectionString, List<ForeRepository> repositories = null, int addDays = 7)
        {
            //根据文件夹内容，获取所有的用户名称及其表名
            if (repositories != null)
                semier = new Semier(dbConnectionString, repositories.ToArray());
            else
                semier = new Semier(dbConnectionString);

            string tables = "All Fore";
            if (repositories != null)
            {
                List<string> table_names = new List<string>();
                foreach (ForeRepository repository in repositories)
                {
                    table_names.Add(repository.TableName);
                }
                tables = string.Join(",", table_names.ToArray());
            }
             Logger.Info(string.Format("初始化Fore表: {0}, 将会重新计算这些表的Semi",tables));
             Log.SetLog(string.Format("初始化Fore表: {0}, 将会重新计算这些表的Semi", tables));

            RainRealRepository autoRainRealRepository = RainRealRepositoryFactory.AutoStationRainRealRepository(dbConnectionString);
            RainRealRepository manualRainRealRepository = RainRealRepositoryFactory.ManualRainRealRepository(dbConnectionString);

            TempRealRepository autoTempRealRepository = TempRealRepositoryFactory.AutoStationTempRealRepository(dbConnectionString);
            TempRealRepository manualTempRealRepository = TempRealRepositoryFactory.ManualTempRealRepository(dbConnectionString);

            DateTime cur = start;
            end = end.AddDays(addDays);
            if (end > DateTime.Now)
                end = DateTime.Now;
            while (cur <= end)
            {
                Console.WriteLine(string.Format("计算 {0} 的Semi", cur.ToString("yyyy-MM-dd")));
                Logger.Info(string.Format("计算 {0} 的Semi", cur.ToString("yyyy-MM-dd")));
                Log.SetLog(string.Format("计算 {0} 的Semi", cur.ToString("yyyy-MM-dd")));

                RainRealEntity[] rainEntities = autoRainRealRepository.GetEntitiesWithDate(cur);
                if (rainEntities.Length == 0)
                {
                    Logger.Info("找不到自动站降水实况数据的记录，无法计算自动站Semi");
                    Log.SetLog("找不到自动站降水实况数据的记录，无法计算自动站Semi");
                }
                foreach (RainRealEntity entity in rainEntities)
                {
                    semier.CalculatSemiWithRainRecord(entity, ForeSemiType.Auto, ForeSemiType.Auto);
                }

                rainEntities = manualRainRealRepository.GetEntitiesWithDate(cur);
                if (rainEntities.Length == 0)
                {
                    Logger.Info("找不到人工站降水实况数据的记录，无法计算人工站Semi");
                    Log.SetLog("找不到人工站降水实况数据的记录，无法计算人工站Semi");
                }
                foreach (RainRealEntity entity in rainEntities)
                {
                    semier.CalculatSemiWithRainRecord(entity, ForeSemiType.Auto, ForeSemiType.Manual);
                }


                TempRealEntity[] tempEntities = autoTempRealRepository.GetEntitiesWithDate(cur);
                if (tempEntities.Length == 0)
                {
                    Logger.Info("找不到自动站气温实况数据的记录，无法计算自动站Semi");
                    Log.SetLog("找不到自动站气温实况数据的记录，无法计算自动站Semi");
                }
                foreach (TempRealEntity entity in tempEntities)
                {
                    semier.CalculatSemiWithTempRecord(entity, ForeSemiType.Auto, ForeSemiType.Auto);
                }

                tempEntities = manualTempRealRepository.GetEntitiesWithDate(cur);
                if (tempEntities.Length == 0)
                {
                    Logger.Info("找不到人工站气温实况数据的记录，无法计算人工站Semi");
                    Log.SetLog("找不到人工站气温实况数据的记录，无法计算人工站Semi");
                }
                foreach (TempRealEntity entity in tempEntities)
                {
                    semier.CalculatSemiWithTempRecord(entity, ForeSemiType.Auto, ForeSemiType.Manual);
                }

                cur = cur.AddDays(1);
            }
        }

        #endregion

        #region 13个预报产品值入库
        /// <summary>
        /// 13个预报产品值入库
        /// </summary>
        /// <param name="dbConnectionString"></param>
        /// <param name="dt">如果日期为最小值时，则表示不按日期筛选</param>
        /// <param name="appStartPath"></param>
        /// <param name="configItem"></param>
        public void ImportForeData(string dbConnectionString, DateTime dt,string appStartPath, string configItem = "//ForeDataFiles")
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(appStartPath + @"\AutoInDatabae.exe.config");
            XmlNode node = doc.SelectSingleNode( configItem);
            string dirPath = node.Attributes["Dir"].Value;
            foreach (XmlNode n in node.ChildNodes)
            {
                string tableName = n.Attributes["TableName"].Value;
                string dir = n.Attributes["FileDir"].Value;
                string description = n.Attributes["Description"].Value;
                string fileFilter = n.Attributes["FileFilter"].Value;
                ImportForeData(dbConnectionString, dt, tableName, dirPath + "\\" + dir, fileFilter, description);
            }
        }

        public void ImportForeData(string dbConnectionString, DateTime dt, string tableName, string dirPath, string fileFilter, string description)
        {
            
            IDataFileFinder finder = new ForeDataFilesFinder();
            ForeDataImporter importer = DataImporterFactory.GetForeDataImporter(dbConnectionString, tableName);

            importer.Log = this.Log;
            Logger.Info(description + "预报文件入库");
            Log.SetLog(description + "预报文件入库");

            SendpointToClock parser = new SendpointToClock();

            foreach (KeyValuePair<string, string[]> pair in finder.GetFileListFromDirectory(dt, dirPath,fileFilter))
            {
                if (pair.Value.Length > 0)
                {
                   
                    foreach (string path in pair.Value)
                    {
                        try
                        {
                            importer.ClearProcessedData();
                            Logger.Info("发现文件：" + path);
                            Log.SetLog("发现文件：" + path);
                            importer.ImportDataFromFileWithDate(path, dt);

                            string filename = Path.GetFileName(path);
                            DateTime date = importer.GetDateTimeByFileName(filename);
                            ForeSendpointType sendpoint = importer.GetSendpointByFileName(filename);
                            ForeClockType clock = parser.GetClockBySendPoint(sendpoint);
                            importer.SaveDataToDataBase(date, clock, sendpoint);

                            if (dt == DateTime.MinValue)//日期为最小值时，表示不按日期筛选,则执行入库后要将文件移动到OldFiles文件夹
                            {
                                FileInfo fi = new FileInfo(path);
                                if (!Directory.Exists(fi.Directory + @"\OldFiles\"))
                                    Directory.CreateDirectory(fi.Directory + @"\OldFiles\");
                                fi.MoveTo(fi.Directory + @"\OldFiles\" + fi.Name);
                            }
                        }
                        catch(Exception ex)
                        {
                            Logger.Error("入库文件[" + path + "]时出错，" + ex.ToString());
                            Log.SetLog("入库文件[" + path + "]时出错，" + ex.ToString());
                        }
                    }

                }
            }



        }
        #endregion

        #region 13个预报产品值重新入库
        /// <summary>
        /// 13个预报产品值重新入库
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        /// <param name="tableName"></param>
        /// <param name="dirPath"></param>
        /// <param name="fileFilter"></param>
        /// <param name="description"></param>
        public void ReImportForeData(DateTime start, DateTime end, string tableName, string dirPath, string fileFilter, string description)
        {
            string dbConnectionString = AppSettings["ConnectionString"];

            List<ForeRepository> repositories = new List<ForeRepository>() { new ForeRepository(dbConnectionString, tableName) };

            DateTime cur = start;
            while (cur <= end)
            {
                Log.SetLog("开始重新导入【"+description+"】" + cur.ToString("yyyy年MM月dd日") + "的数据!");

                ImportForeData(dbConnectionString, cur, tableName, dirPath, fileFilter, description);

                Log.SetLog("重新导入【" + description + "】" + cur.ToString("yyyy年MM月dd日") + "的数据操作结束!");

                cur = cur.AddDays(1);
            }

            Log.SetLog("开始重新计算【" + description + "】" + start.ToString("yyyy年MM月dd日") + "至" + end.ToString("yyyy年MM月dd日") + "的数据的SEMI!");

            ReCalSemi(start, end, dbConnectionString, repositories);

            Log.SetLog("重新计算【" + description + "】" + start.ToString("yyyy年MM月dd日") + "至" + end.ToString("yyyy年MM月dd日") + "的数据的SEMI操作结束!");
        }
        #endregion

        #region 13个预报产品值减去平均误差后得到的新预报产品入库
        /// <summary>
        /// 13个预报产品值减去平均误差后得到的新预报产品入库
        /// </summary>
        /// <param name="dbConnectionString"></param>
        /// <param name="dt"></param>
        /// <param name="appStartPath"></param>
        /// <param name="configItem"></param>
        public void ImportNewForeData(string dbConnectionString, DateTime dt, string appStartPath, string configItem = "//ForeDataFiles")
        {
            XmlDocument doc = new XmlDocument();
            DirectoryInfo di = new DirectoryInfo(appStartPath);
            FileInfo[] fis = di.GetFiles("*.exe.config");
            doc.Load(fis[0].FullName);
            XmlNode node = doc.SelectSingleNode(configItem);
            foreach (XmlNode n in node.ChildNodes)
            {
                string tableName = n.Attributes["TableName"].Value;
                string description = n.Attributes["Description"].Value;
                Logger.Info(description + "最优预报值入库");
                Log.SetLog(description + "最优预报值入库");

                ForeRepository foreRepository = new ForeRepository(dbConnectionString, tableName);
                ForeEntity[] foreEntitys = foreRepository.InquerySemiWithDateRange(dt, dt, ForeFactorType.maxt, ForeSemiType.Auto);
                ImportNewForeData(foreEntitys, dt, dbConnectionString, tableName);
                foreEntitys = foreRepository.InquerySemiWithDateRange(dt, dt, ForeFactorType.mint, ForeSemiType.Auto);
                ImportNewForeData(foreEntitys, dt, dbConnectionString, tableName);
            }

            // 计算新预报产品的Semi
            CalculateNewForeSemi(dbConnectionString, dt, appStartPath);
        }

        /// <summary>
        /// 13个预报产品值减去平均误差后得到的新预报产品入库
        /// </summary>
        /// <param name="foreEntitys"></param>
        /// <param name="dt"></param>
        /// <param name="dbConnectionString"></param>
        /// <param name="tableName"></param>
        private void ImportNewForeData(ForeEntity[] foreEntitys, DateTime dt, string dbConnectionString, string tableName)
        {
            ForeRepository foreRepository1 = ForeRepositoryFactory.ForeDataRepository(dbConnectionString, tableName);
            ForeRepository foreRepository2 = ForeRepositoryFactory.ForeDataRepository(dbConnectionString, "New_" + tableName);

            foreach (ForeEntity entity in foreEntitys)
            {
                Dictionary<string, float> averageDeviation = AverageDeviation(foreRepository1, dt, entity);

                Dictionary<string, float> foreValues = new Dictionary<string, float>();

                foreach (string value in entity.Value.Split(new char[] { '|' }))
                {
                    if (value == "") continue;
                    string[] oserial = value.Split(new char[] { ';' });
                    if (oserial.Length < 2) continue;
                    float _value = -1000;
                    float.TryParse(oserial[1].ToString(), out _value);

                    foreValues.Add(oserial[0], _value);
                }

                entity.Value = "";
                entity.Semi = "";
                //预报值减去前10天平均误差
                foreach (KeyValuePair<string, float> pair in foreValues)
                {
                    float newValue = 999.9f;
                    if (pair.Value != 999.9f && pair.Value < 45f && pair.Value > -15f)
                    {
                        if (averageDeviation.Keys.Contains(pair.Key))
                            newValue = foreValues[pair.Key] - averageDeviation[pair.Key];
                    }

                    if (entity.Value != "")
                        entity.Value += "|";
                    entity.Value += pair.Key + ";" + newValue.ToString();
                }

                if (foreRepository2.IsForeRecordExist(entity))
                    foreRepository2.UpdateSemiAndValue(entity);
                else
                    foreRepository2.InsertForeRecord(entity);
            }

        }

        /// <summary>
        /// 计算前10天各个站点的温度平均误差
        /// </summary>
        /// <param name="foreRepository"></param>
        /// <param name="dt"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        private Dictionary<string, float> AverageDeviation(ForeRepository foreRepository,DateTime dt, ForeEntity entity)
        {
            ForeClockType[] clocks = new ForeClockType[] { entity.Clock };
            ForeSpanType span = entity.Span;
            ForeSendpointType[] sendpoints = new ForeSendpointType[] { entity.Sendpoint };
            ForeFactorType type = entity.Type;
            ForeSemiType semitype = entity.Semitype;
            int days = DeviationDays(span);
            DateTime end = dt.AddDays(0 - days);
            DateTime start = end.AddDays(-10);
           

            ForeEntity[] deviationForeEntitys = foreRepository.InquerySemiWithDateRange(start, end, clocks, span, sendpoints, type, semitype, false);
            Dictionary<string, float> averageDeviation = AverageDeviation(deviationForeEntitys);

            return averageDeviation;
        }
        /// <summary>
        /// 计算各个站点的温度平均误差
        /// </summary>
        /// <param name="ForeEntitys"></param>
        /// <returns></returns>
        private Dictionary<string, float> AverageDeviation(ForeEntity[] ForeEntitys)
        {
            Dictionary<string, float> averageDeviation = new Dictionary<string, float>();
            Dictionary<string, List<float>> offsetValues = new Dictionary<string, List<float>>();
            float[] ErrorValues = new float[] { -100, -101, -102, -103, -104, -105 };//错误代码
           
           
            foreach (ForeEntity entity in ForeEntitys)
            {
                foreach (string semi in entity.Semi.Split(new char[] { '|' }))
                {
                    if (semi == "") continue;
                    string[] oserial = semi.Split(new char[] { ';' });
                    if (oserial.Length < 2) continue;
                    float _offset = -1000;
                    float.TryParse(oserial[1].ToString(), out _offset);

                    if (_offset == 999.9f || _offset > 45f || _offset < -15f) continue;//不是正常温度值，跳过
                    if (ErrorValues.Contains(_offset)) continue;//错误代码，跳过


                    if (!offsetValues.Keys.Contains(oserial[0]))
                        offsetValues.Add(oserial[0], new List<float>());

                    offsetValues[oserial[0]].Add(_offset);
                        
                }
            }

            foreach (KeyValuePair<string, List<float>> pair in offsetValues)
                averageDeviation.Add(pair.Key, CalculateAverageDeviation(pair.Value));

            return averageDeviation;
        }

        /// <summary>
        /// 温度平均误差
        /// </summary>
        /// <returns>误差值</returns>
        private float CalculateAverageDeviation(List<float> OffsetList)
        {
            if (OffsetList.Count == 0)
                return 0f;

            float all = 0f;
            foreach (float f in OffsetList)
                all += f;
            return all / OffsetList.Count;
        }

        /// <summary>
        ///计算当前时效具有实况值的预报应该向前倒推的天数
        /// </summary>
        /// <param name="span"></param>
        /// <returns></returns>
        private int DeviationDays(ForeSpanType span)
        {
            string strSpan = span.ToString().Replace("FD", "");
            string[] hours = strSpan.Split('_');
            int start = Convert.ToInt32(hours[0]);
            int end = Convert.ToInt32(hours[1]);


            int days= (end - start) / 24;
            days = days == 0 ? 1 : days;
            return days;
        }

       
        /// <summary>
        /// 计算新预报产品的Semi
        /// </summary>
        /// <param name="dbConnectionString"></param>
        /// <param name="dt"></param>
        /// <param name="appStartPath"></param>
        /// <param name="configItem"></param>
        public void CalculateNewForeSemi(string dbConnectionString, DateTime dt, string appStartPath, string configItem = "//ForeDataFiles")
        {
            List<ForeRepository> repositories = new List<ForeRepository>();

            XmlDocument doc = new XmlDocument();
            DirectoryInfo di = new DirectoryInfo(appStartPath);
            FileInfo[] fis = di.GetFiles("*.exe.config");
            doc.Load(fis[0].FullName);
            XmlNode node = doc.SelectSingleNode(configItem);
            foreach (XmlNode n in node.ChildNodes)
                repositories.Add(new ForeRepository(dbConnectionString, "New_" + n.Attributes["TableName"].Value));


            semier = new Semier(dbConnectionString, repositories.ToArray());

            string tables = "All Fore";
            if (repositories != null)
            {
                List<string> table_names = new List<string>();
                foreach (ForeRepository repository in repositories)
                    table_names.Add(repository.TableName);
                tables = string.Join(",", table_names.ToArray());
            }
            Logger.Info(string.Format("初始化Fore表: {0}, 将会重新计算这些表的Semi", tables));
            Log.SetLog(string.Format("初始化Fore表: {0}, 将会重新计算这些表的Semi", tables));


            TempRealRepository autoTempRealRepository = TempRealRepositoryFactory.AutoStationTempRealRepository(dbConnectionString);
            TempRealRepository manualTempRealRepository = TempRealRepositoryFactory.ManualTempRealRepository(dbConnectionString);


            Console.WriteLine(string.Format("计算 {0} 的Semi", dt.ToString("yyyy-MM-dd")));
            Logger.Info(string.Format("计算 {0} 的Semi", dt.ToString("yyyy-MM-dd")));
            Log.SetLog(string.Format("计算 {0} 的Semi", dt.ToString("yyyy-MM-dd")));


            TempRealEntity[] tempEntities = autoTempRealRepository.GetEntitiesWithDate(dt);
            if (tempEntities.Length == 0)
            {
                Logger.Error("找不到自动站气温实况数据的记录，无法计算Semi");
                Log.SetLog("找不到自动站气温实况数据的记录，无法计算Semi");
            }

            foreach (TempRealEntity entity in tempEntities)
                semier.CalculatSemiWithTempRecord(entity, ForeSemiType.Auto, ForeSemiType.Auto);


            tempEntities = manualTempRealRepository.GetEntitiesWithDate(dt);
            if (tempEntities.Length == 0)
            {
                Logger.Error("找不到人工站气温实况数据的记录，无法计算Semi");
                Log.SetLog("找不到人工站气温实况数据的记录，无法计算Semi");
            }
            foreach (TempRealEntity entity in tempEntities)
                semier.CalculatSemiWithTempRecord(entity, ForeSemiType.Auto, ForeSemiType.Manual);

        }
        #endregion

        #region 计算最优集成预报并入库
        /// <summary>
        /// 计算最优集成预报并入库
        /// </summary>
        /// <param name="dbConnectionString"></param>
        /// <param name="dt"></param>
        /// <param name="appStartPath"></param>
        /// <param name="configItem"></param>
        /// <param name="WthTable"></param>
        public void ImportOptimalForeData(string dbConnectionString, DateTime dt, string appStartPath, string configItem = "//ForeDataFiles", string WthTable = "EnsembleFore")
        {

            List<float> Weights = new List<float>();
            foreach (string weight in AppSettings["Weights"].Split(','))
                Weights.Add(float.Parse(weight));

            //int Count = Weights.Count;

            Dictionary<string, ForeEntity> Parameters = new Dictionary<string, ForeEntity>();
            Dictionary<string, Dictionary<string, DataTable>> OffsetsAndValues = new Dictionary<string, Dictionary<string, DataTable>>();
            DataTable OffsetsAndValuesTable = new DataTable();
            OffsetsAndValuesTable.Columns.Add("offset", typeof(float));
            OffsetsAndValuesTable.Columns.Add("value", typeof(float));

            XmlDocument doc = new XmlDocument();
            DirectoryInfo di = new DirectoryInfo(appStartPath);
            FileInfo[] fis = di.GetFiles("*.exe.config");
            doc.Load(fis[0].FullName);
            XmlNode node = doc.SelectSingleNode(configItem);
            foreach (XmlNode n in node.ChildNodes)
            {
                Log.SetLog("正在生成【"+n.Attributes["Description"].Value+"】集成最优预报数据！");
                Logger.Info("正在生成【" + n.Attributes["Description"].Value + "】集成最优预报数据！");

                string tableName = n.Attributes["TableName"].Value;
                ForeRepository foreRepository1 = new ForeRepository(dbConnectionString, tableName);
                ForeRepository foreRepository2 = new ForeRepository(dbConnectionString, "New_" + tableName);

                PrepareForeData(Parameters, OffsetsAndValues, OffsetsAndValuesTable,
                                          foreRepository1.InquerySemiWithDateRange(dt, dt, ForeFactorType.maxt, ForeSemiType.Auto), foreRepository1, dt);
                PrepareForeData(Parameters, OffsetsAndValues, OffsetsAndValuesTable,
                                          foreRepository1.InquerySemiWithDateRange(dt, dt, ForeFactorType.mint, ForeSemiType.Auto), foreRepository1, dt);
                PrepareForeData(Parameters, OffsetsAndValues, OffsetsAndValuesTable,
                                          foreRepository2.InquerySemiWithDateRange(dt, dt, ForeFactorType.maxt, ForeSemiType.Auto), foreRepository2, dt);
                PrepareForeData(Parameters, OffsetsAndValues, OffsetsAndValuesTable,
                                          foreRepository1.InquerySemiWithDateRange(dt, dt, ForeFactorType.mint, ForeSemiType.Auto), foreRepository2, dt);

            }


            ForeRepository foreRepository3 = ForeRepositoryFactory.ForeDataRepository(dbConnectionString, "OptimalFore");

            foreach (KeyValuePair<string, ForeEntity> pair1 in Parameters)
            {
                List<string> OptimalValue = new List<string>();//最优集成预报值

                foreach (string key in OffsetsAndValues[pair1.Key].Keys.ToList<string>() )
                {
                    DataTable dataTable = OffsetsAndValues[pair1.Key][key];
                    dataTable.DefaultView.Sort = "offset ASC";//误差值按照从小到大排序
                    DataTable dtTemp = dataTable.DefaultView.ToTable();
                    OffsetsAndValues[pair1.Key][key] = dtTemp;

                    float newValue = CalculateOptimalForeData(dtTemp, Weights);//带入权重算出集成预报值
                    OptimalValue.Add(key + ";" + newValue.ToString());
                }

                Parameters[pair1.Key].Value = string.Join("|", OptimalValue.ToArray());

                if (foreRepository3.IsForeRecordExist(Parameters[pair1.Key]))
                    foreRepository3.UpdateSemiAndValue(Parameters[pair1.Key]);
                else
                    foreRepository3.InsertForeRecord(Parameters[pair1.Key]);//将最优集成预报值添加到数据库

               
            }

            ForeRepository foreRepository4 = new ForeRepository(dbConnectionString, WthTable);
            ForeEntity[] foreEntitys = foreRepository4.InquerySemiWithDateRange(dt, dt, ForeFactorType.wth, ForeSemiType.Auto);
            foreach (ForeEntity foreEntity in foreEntitys)//天气数据用欧洲集成报
            {
                if (foreRepository3.IsForeRecordExist(foreEntity))
                    foreRepository3.UpdateSemiAndValue(foreEntity);
                else
                    foreRepository3.InsertForeRecord(foreEntity);

               
            }
        }

        /// <summary>
        /// 将当前日期的26个预报产品数据进行预处理
        /// </summary>
        /// <param name="Parameters">存放最终需要入库的ForeEntity记录</param>
        /// <param name="OffsetsAndValues">存放26个预报产品中各个站点预报偏差值</param>
        /// <param name="OffsetsAndValuesTable">预报偏差模板表</param>
        /// <param name="foreEntitys">当前日期的ForeEntity记录组</param>
        /// <param name="dt">当前日期</param>
        public void PrepareForeData(Dictionary<string, ForeEntity> Parameters,
                                    Dictionary<string, Dictionary<string, DataTable>> OffsetsAndValues,
                                    DataTable OffsetsAndValuesTable,
                                    ForeEntity[] foreEntitys, ForeRepository foreRepository, DateTime dt)
        {
            foreach (ForeEntity entity in foreEntitys)
            {

                string key = string.Format("{0};{1};{2};{3};{4}",entity.Date, entity.Span, entity.Clock, entity.Sendpoint, entity.Type);
                ForeEntity parameter = (ForeEntity)entity.Clone();
                parameter.Value = "";
                parameter.Semi = "";
                if (!Parameters.Keys.Contains(key))
                    Parameters.Add(key, parameter);
                if (!OffsetsAndValues.Keys.Contains(key))
                    OffsetsAndValues.Add(key, new Dictionary<string, DataTable>());

                Dictionary<string, float> offsets = AverageDeviation(foreRepository, dt, entity);

                Dictionary<string, float> values = new Dictionary<string, float>();

                float[] ErrorValues = new float[] { -100, -101, -102, -103, -104, -105 };//错误代码

                foreach (string s in entity.Value.Split(new char[] { '|' }))
                {
                    if (s == "") continue;

                    string[] oserial = s.Split(new char[] { ';' });
                    float _value = -1000;
                    float.TryParse(oserial[1].ToString(), out _value);

                    if (_value == 999.9f || _value > 45f || _value < -15f) continue;//不是正常温度值，跳过
                    if (ErrorValues.Contains(_value)) continue;//错误代码，跳过

                    values.Add(oserial[0], _value);

                }

                foreach (KeyValuePair<string, float> pair in offsets)
                {
                    if (!OffsetsAndValues[key].Keys.Contains(pair.Key))
                        OffsetsAndValues[key].Add(pair.Key, OffsetsAndValuesTable.Clone());

                    if (offsets.ContainsKey(pair.Key) && values.ContainsKey(pair.Key))
                        OffsetsAndValues[key][pair.Key].Rows.Add(new object[] { offsets[pair.Key], values[pair.Key] });
                }


            }
        }

        /// <summary>
        /// 计算最优集成预报值
        /// </summary>
        /// <param name="OffsetsAndValuesTable"></param>
        /// <param name="Weights"></param>
        /// <returns></returns>
        public float CalculateOptimalForeData(DataTable OffsetsAndValuesTable, List<float> Weights)
        {
            float totalOffset = 0f;
            float totalWeight = 0f;
            float totalValue = 0f;

            for (int i = 0; i < Weights.Count && i < OffsetsAndValuesTable.Rows.Count; i++)//算出前n个误差值总和
            {
                totalOffset += (float)OffsetsAndValuesTable.Rows[i]["offset"];
            }

            for (int i = 0; i < Weights.Count && i < OffsetsAndValuesTable.Rows.Count; i++)
            {
                totalWeight += totalOffset / Weights[i];//算出权重总和

                totalValue += (float)OffsetsAndValuesTable.Rows[i]["value"] * (totalOffset / Weights[i]);//带入权重计算预报值总和
            }


            return totalValue / totalWeight;//算出加权后的平均值

        }
        #endregion
    }
}
